#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>


// decode
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/socket.h>
#include "opencv2/opencv.hpp"
#include "Ffmpeg_Decode.h"


#include <list>
#include <thread>
#include <utility>
#include <mutex>

using namespace cv;

#define DEBUG
//
long consumptiontime;
long playframecount = 0;

/* start udp */
//socekt

Socket_Udp_Server_Info * initUdpServer()
{
    int i = 0;
    Socket_Udp_Server_Info * mSocketInfo = (Socket_Udp_Server_Info *)malloc(sizeof(Socket_Udp_Server_Info));

    mSocketInfo->serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (mSocketInfo->serSocket == -1)
    {
        printf("socket error !");
        return 0;
    }

    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(8888);
    serAddr.sin_addr.s_addr = INADDR_ANY;
    if (bind(mSocketInfo->serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == -1)
    {
        printf("bind error !");
        close(mSocketInfo->serSocket);
        return 0;
    }
    else
    {
        printf("bind succeed  ! \n");
    }
    mSocketInfo->nAddrLen[0] = sizeof(sockaddr_in);

    return mSocketInfo;
}

// 结束时候调用
void unitUdpServer(Socket_Udp_Server_Info * mSocketInfo)
{
    close(mSocketInfo->serSocket);
}


/* start decode */




MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    /*udp decode etc. init*/

    mSocketUdpServerInfo = initUdpServer();

    int info[3];
    int flag = 0;
    int ret = 0;
    printf("wait for video flag...\n");
    ret = recvfrom(mSocketUdpServerInfo->serSocket, (char *)info, sizeof(int) * 3, 0,
        (sockaddr *)&mSocketUdpServerInfo->remoteAddr[0], &mSocketUdpServerInfo->nAddrLen[0]);
    recv_width = info[0];
    recv_height = info[1];
    recv_channel = info[2];
    recv_bufferSize = recv_width * recv_height * recv_channel;
    flag = 1;

    printf("flag re succeed w=%d h=%d c=%d \n", recv_width, recv_height, recv_channel);
    if (ret == sizeof(int))
    {
        printf("flag re succeed \n");
    }

    ffmpegobj.Ffmpeg_Decoder_Init();

    //std::thread decode_thread(decode_show_func, std::ref(ffmpegobj));

    //std::thread udp_recv_thread(udp_recv_func, mSocketUdpServerInfo);
    /*udp decode etc. init end*/



    /*qt ui ready*/
    ui->setupUi(this);
    printf("ui->setupUI\n");

    fps = 15;

    //cv::Mat imageTemp;
    //video>>imageTemp;
    //cv::Mat imageTemp(recv_height, recv_width, CV_8UC3);
    cv::Mat imageTemp = cv::Mat::zeros(recv_height, recv_width, CV_8UC3);
    ShowImage(imageTemp);
    printf("[init] ShowImage(zeroMat)\n");

    printf("[init] connect buttons\n");

    connect(ui->pushButton_play, &QPushButton::clicked, [&]() {
        on_pushButton_play_clicked();
    });

    connect(ui->pushButton_pause, &QPushButton::clicked, [&]() {
        on_pushButton_pause_clicked();
    });

    printf("[init] create timers and connect\n");
    timer=new QTimer(this);
    ui-> label_image->setScaledContents(true);//可以使图片完全按QWidget缩放，而不保持原视频比例
    connect(timer,SIGNAL(timeout()),this,SLOT(ReadFrame()));


    timer1=new QTimer(this);
    connect(timer1,SIGNAL(timeout()),this,SLOT(decode_show_func()));
    //std::thread decode_thread(decode_show_func, std::ref(ffmpegobj));

    timer2=new QTimer(this);
    connect(timer2,SIGNAL(timeout()),this,SLOT(udp_recv_func()));
    //std::thread udp_recv_thread(udp_recv_func, mSocketUdpServerInfo);

    printf("[init] end\n");
}

void MainWindow::ShowImage(cv::Mat& mat)
{
     //方法二，按比例缩放
    QImage image=MatToQImage2(mat);
    QPixmap pixmap = QPixmap::fromImage(image);
    int width = ui->label_image->width();
    int height = ui->label_image->height();
    QPixmap fitpixmap = pixmap.scaled(width, height, 
                Qt::KeepAspectRatio, Qt::SmoothTransformation);
    //按比例缩放
    ui->label_image->setPixmap(fitpixmap);
}

//图像格式转换
QImage MainWindow::MatToQImage2(const cv::Mat &mat)
{
    QImage img;
    int chana = mat.channels();
    //依据通道数不同，改变不同的装换方式
    if(3 == chana ){
        //调整通道次序
        cv::cvtColor(mat,mat,cv::COLOR_BGR2RGB);
        img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_RGB888);
    }
    else if(4 == chana )
    {
        //argb
        img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_ARGB32);
    }
    else {
    //单通道，灰度图
    img = QImage( mat.cols, mat.rows , QImage::Format_Indexed8);
    uchar * matdata = mat.data;
        for(int row = 0 ; row <mat.rows ; ++row )
        {
            uchar* rowdata = img.scanLine( row );
            memcpy(rowdata,matdata ,mat.cols);
        matdata+=mat.cols;
        }
    }
    return img;
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_play_clicked()
{
    is_play = true;
    timer->start(1000/fps);
    timer1->start(1);
    timer2->start(1);
}

void MainWindow::on_pushButton_pause_clicked()
{
    is_play = false;
    timer->stop();
    timer1->stop();
    timer2->stop();
}



void MainWindow::ReadFrame()
{

    //cv::Mat frame_now;
    //video >> frame_now;//读帧进frame
    //if (frame_now.empty())
    if (list_matbuff.empty())
    {
        printf("[Warn] Timer->ReadFrame()::list_matbuff.empty()\n");
        usleep(5);
        //QMessageBox::information(this, tr("warning"), tr("the video is end!"));
        //timer->stop();
    }
    else {
        printf("[OK] Timer->ReadFrame()::ShowImage ... wait");

        ShowImage(list_matbuff.front());
        printf(" ... ok!");

        mtx_rgblist.lock();
        list_matbuff.pop_front();
        mtx_rgblist.unlock();
    }
}

//void udp_recv_func( Socket_Udp_Server_Info * mSocketUdpServerInfo )
void MainWindow::udp_recv_func()
{
    //while(1) {
        if(!is_play)
        {
            //usleep(5);
            if(!list_pbuff.empty()) list_pbuff.clear();
            if(!list_matbuff.empty()) list_matbuff.clear();
            
            printf("[WARN] recv_func::is_play=false\n");

            return ;
            //continue;
        }

        //usleep(1);
        char * revBuffer = (char *)malloc(sizeof(char) * recv_bufferSize);
        size_t ret = recvfrom(mSocketUdpServerInfo->serSocket, revBuffer, recv_bufferSize, 0,
                                      (sockaddr *)&mSocketUdpServerInfo->remoteAddr[0], &mSocketUdpServerInfo->nAddrLen[0]);


        printf("[OK] recv ret = %d \n", ret);
        //ffmpegobj.nDataLen = recv(mSocketTcpServerInfo->sClient[0], revBuffer, recv_bufferSize, 0);
        //memcpy(ffmpegobj.filebuf, revBuffer, ffmpegobj.nDataLen);
        char * pbuf = (char *)malloc(sizeof(char) * ret + 1);
        memset(pbuf, 0, ret + 1);
        memcpy(pbuf, revBuffer, ret);

        mtx_list.lock();
        list_pbuff.push_back(std::pair<char*, int>(pbuf, ret));
        mtx_list.unlock();
    //}
}

/* end udp */

//thread func
//void decode_show_func(Ffmpeg_Decoder& ffmpegobj)
void MainWindow::decode_show_func()
{
    //while(true){
        //usleep(1);
        if(list_pbuff.empty()) {
            //usleep(5);
            printf("[WARN] decode_func::lis_pbuff is empty!\n");
            return ;
            //continue;
        }
//Houlc

        mtx_list.lock();

        char* pbuff = list_pbuff.front().first;
        int ilen = list_pbuff.front().second;

        memcpy(ffmpegobj.filebuf, pbuff, ilen);
        ffmpegobj.nDataLen = ilen;

        free(pbuff);
        list_pbuff.pop_front();

        mtx_list.unlock();

        //must memcpy(ffmpegobj), and set it's len before
        printf("[OK] decode_func::decode_show() ... wait");
        //decode_show(ffmpegobj);
        decode_show();
        printf(" ... ok!\n");
    //}

}

/* end decode */

//Ffmpeg_Decoder ffmpegobj;
//decode func
//void decode_show(Ffmpeg_Decoder &ffmpegobj)
void MainWindow::decode_show()
{
   if (ffmpegobj.nDataLen <= 0)
   {
       //houlc
       printf("[Error]ffmpegobj.nDataLen <=0 \n");
    //break;
       return ;
   }
   else
   {
    ffmpegobj.haveread = 0;
    while (ffmpegobj.nDataLen > 0)
    {
        //usleep(1);
        printf("[houlc-debug] av_parser_parse2..start..\n");
        int nLength = av_parser_parse2(ffmpegobj.avParserContext, ffmpegobj.c, &ffmpegobj.yuv_buff,
            &ffmpegobj.nOutSize, ffmpegobj.filebuf + ffmpegobj.haveread, ffmpegobj.nDataLen, 0, 0, 0);//查找帧头
        printf("[houlc-debug] av_parser_parse2..ok..ret=%d\n", ffmpegobj.nOutSize);
        ffmpegobj.nDataLen -= nLength;//查找过后指针移位标志
        ffmpegobj.haveread += nLength;
        if (ffmpegobj.nOutSize <= 0)
        {

            break;
        }
        ffmpegobj.avpkt.size = ffmpegobj.nOutSize;//将帧数据放进包中
        ffmpegobj.avpkt.data = ffmpegobj.yuv_buff;
        playframecount++;

        //usleep(1);
        printf("[houlc-debug] avcodec_decode_video2..start..\n");
        ffmpegobj.decodelen = avcodec_decode_video2(ffmpegobj.c, ffmpegobj.m_pYUVFrame, &ffmpegobj.piclen, &ffmpegobj.avpkt);//解码
        printf("[houlc-debug] avcodec_decode_video2..ret:%d.\n", ffmpegobj.decodelen);

        printf("[houlc-debug] AVERROR_BUG=%ld\n", AVERROR_BUG);
           if(ffmpegobj.piclen<0)
        {
            printf("\n[houlc]decode len < 0!!!!\n");
            break;
        }
        if (ffmpegobj.piclen)
        {
            if (ffmpegobj.finishInitScxt == false)//初始化格式转换函数
            {
                ffmpegobj.finishInitScxt = true;
                ffmpegobj.scxt = sws_getContext(ffmpegobj.c->width, ffmpegobj.c->height, ffmpegobj.c->pix_fmt,
                    ffmpegobj.c->width, ffmpegobj.c->height, AV_PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);

                int width = ffmpegobj.c->width;
                int height = ffmpegobj.c->height;
                ffmpegobj.yuv_buff = new uint8_t[width * height * 2];//初始化YUV图像数据区
                ffmpegobj.rgb_buff = new uint8_t[width * height * 3];//初始化RGB图像帧数据区
                ffmpegobj.data[0] = ffmpegobj.rgb_buff;
                ffmpegobj.linesize[0] = width * 3;
            }
            if (ffmpegobj.scxt != NULL)
            {
                //usleep(1);
                //YUV转rgb
                sws_scale(ffmpegobj.scxt, ffmpegobj.m_pYUVFrame->data, ffmpegobj.m_pYUVFrame->linesize, 0,
                    ffmpegobj.c->height, ffmpegobj.data, ffmpegobj.linesize);

                //usleep(1);
                //ffmpegobj.Ffmpeg_Decoder_Show((char*)ffmpegobj.rgb_buff, ffmpegobj.c->width, ffmpegobj.c->height);//解码图像显示

                Mat rgbmat = Mat(ffmpegobj.c->height, ffmpegobj.c->width, CV_8UC3, (char*)ffmpegobj.rgb_buff);
                mtx_rgblist.lock();
                list_matbuff.push_back(rgbmat);
                mtx_rgblist.unlock();
            }
        }
    }
   }
}
